﻿using HtmlAgilityPack;
using Microsoft.SharePoint.Client;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Management.Automation;

namespace SharePointOnlineCmdlets
{
    /// <summary>
    /// Installs (trusts) an App in a web.
    /// </summary>
    [Cmdlet(VerbsLifecycle.Install, "SPOApp")]
    public class InstallSPOApp : PSCmdlet
    {
        [Parameter(Mandatory = true, ValueFromPipeline = true)]
        public Web Web { get; set; }

        [Parameter(Mandatory = true)]
        public string AppCatalogUrl { get; set; }

        [Parameter(Mandatory = true)]
        public string ClientId { get; set; }

        [Parameter(Mandatory = true)]
        public string AppPackage { get; set; }

        private ClientContext Context;

        protected override void ProcessRecord()
        {
            Context = (ClientContext)Web.Context;
            Context.Load(Web, i=> i.ServerRelativeUrl);
            Context.Load(Context.Site, i => i.ServerRelativeUrl);
            Context.ExecuteQuery();
            var url = Context.Site.ServerRelativeUrl == "/" ? Context.Url : Context.Url.Replace(Context.Site.ServerRelativeUrl, "");
            var host = new Uri(url);
            var authRealm = GetAuthenticationRealm(url);
            var appCatalogId = GetAppCatalogIdForApp(url);
            var requestUrl = string.Format("{0}/_layouts/15/addanapp.aspx", url.TrimEnd('/') + Web.ServerRelativeUrl);
            var claimsClient = new SPOWebClient(host, (SharePointOnlineCredentials)Context.Credentials);
            var reponse = claimsClient.DownloadString(requestUrl);
            var postData = new NameValueCollection();
            HtmlDocument doc = new HtmlDocument();
            doc.LoadHtml(reponse);
            foreach (HtmlNode input in doc.DocumentNode.SelectNodes("//input"))
            {
                var name = input.Attributes["name"];
                if (name != null)
                {
                    if (name.Value == "__EVENTVALIDATION" || name.Value == "__REQUESTDIGEST" ||
                        name.Value == "__VIEWSTATE")
                    {
                        postData.Add(input.Attributes["name"].Value,
                                     input.Attributes["value"] == null ? "" : input.Attributes["value"].Value);
                    }
                }
            }
            postData.Add("task", "AppDownload");
            postData.Add("oID", String.Format("i:0i.t|ms.sp.ext|{0}@{1}", ClientId, authRealm));
            postData.Add("catalog", "1");
            postData.Add("appid", appCatalogId);

            claimsClient.Headers.Add(HttpRequestHeader.Host, Context.Url.Replace("https://", "").Split('/').First());
            claimsClient.Headers.Add(HttpRequestHeader.Referer, requestUrl);
            claimsClient.Headers.Add(HttpRequestHeader.Accept, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
            claimsClient.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip, deflate");
            claimsClient.Headers.Add(HttpRequestHeader.AcceptLanguage, "en-US,en;q=0.5");
            claimsClient.Headers.Add(HttpRequestHeader.UserAgent, "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0");
            //Do the post
            claimsClient.UploadValues(requestUrl, postData);
        }

        private string GetAuthenticationRealm(string hostUrl)
        {
            var host = new Uri(hostUrl);
            var requestUrl = string.Format("{0}/_layouts/15/appprincipals.aspx", hostUrl.TrimEnd('/') + "/sites/contenttypehub");
            var claimsClient = new SPOWebClient(host, (SharePointOnlineCredentials)Context.Credentials);
            var reponse = claimsClient.DownloadString(requestUrl);
            HtmlDocument doc = new HtmlDocument();
            doc.LoadHtml(reponse);
            var item = doc.DocumentNode.SelectSingleNode("//table[@id='ctl00_PlaceHolderMain_GridApps']//td[position()=3]");
            return item.InnerText.Split('@').Last().Trim('\r', '\t', '\n');
        }

        private string GetAppCatalogIdForApp(string hostUrl)
        {
            using (ClientContext ctx = new ClientContext(hostUrl.TrimEnd('/') + AppCatalogUrl))
            {
                ctx.Credentials = Context.Credentials;
                var file = ctx.Web.GetFileByServerRelativeUrl(AppCatalogUrl.TrimEnd('/') + "/appcatalog/" + AppPackage);
                ctx.Load(file);
                ctx.ExecuteQuery();
                return file.ContentTag.Split(',').First().Trim('{', '}');
            }
        }
    }

    /// <summary>
    /// Adds an app into the app catalog
    /// </summary>
    [Cmdlet(VerbsCommon.Add, "SPOApp")]
    public class AddSPOApp : PSCmdlet
    {
        [Parameter(ValueFromPipeline = true), ValidateNotNullOrEmpty]
        public ClientContext Context { get; set; }

        [Parameter, ValidateNotNullOrEmpty]
        public string AppCatalogUrl { get; set; }

        [Parameter, ValidateNotNullOrEmpty]
        public string FilePath { get; set; }

        protected override void ProcessRecord()
        {
            Context.Load(Context.Site, i => i.ServerRelativeUrl);
            Context.ExecuteQuery();
            var url = Context.Site.ServerRelativeUrl == "/" ? Context.Url : Context.Url.Replace(Context.Site.ServerRelativeUrl,"");
            var ctx = new ClientContext(url + AppCatalogUrl);
            ctx.Credentials = Context.Credentials;
            ctx.RequestTimeout = 3600000;
            var folder = ctx.Site.RootWeb.GetFolderByServerRelativeUrl("AppCatalog");
            ctx.Load(folder);
            ctx.ExecuteQuery();
            var fileUrl = folder.ServerRelativeUrl + "/" + Path.GetFileName(FilePath);

            using (var fileStream = new FileStream(FilePath, FileMode.Open))
            {
                Microsoft.SharePoint.Client.File.SaveBinaryDirect(ctx, fileUrl, fileStream, true);
            }
        }
    }
}
